import os
import subprocess
import psutil

Content = {
    "worker_processes": "1",
    "worker_connections": 1024,
    "include": "mime.types",
    "default_type": "application/octet-stream",
    "sendfile": "on",
    "keepalive_timeout": 65,
    "error_page": "500 502 503 504  /50x.html",
    "location": "/50x.html {   root    html;}"
}
'''
************************************************************
        运行后，窗口会持续存在，如果窗口被关闭了，那么nginx的服务将会随之关闭
        只要这两个实例的nginx_path相同
        那么实例一个NginxManger并启动后，在这个实例的运行窗口没有被关闭的情况下
        在实例一个NginxManger可以关闭nginx服务，原先的运行窗口也会随之关闭
        如：
            在test_1.py 运行NginxManger(nginx_path='aaa').Run_nginx()
            在test_2.py 运行NginxManger(nginx_path='aaa').Stop_nginx()是可以关闭nginx的

************************************************************
'''


class NginxManger:
    def __init__(self, nginx_path, content=None):
        self.nginx_path = nginx_path
        self.config_path = os.path.join(self.nginx_path, 'conf', 'nginx.conf')
        self.error_log = os.path.join(self.nginx_path, 'logs', 'error.log')
        self.access_log = os.path.join(self.nginx_path, 'logs', 'access.log')
        self.mime_types_file = os.path.join(self.nginx_path, 'conf', 'mime.types')

        if not content: self.content = Content
        self.listen_port = None
        self.server_name = None
        self.ssl_content = None
        self.token_content=None
        self.token_url=None
        self.http_redirect = False
        self.location = []
        self.tem_content = '           proxy_set_header Cookie $http_cookie;\n' \
                           '             proxy_set_header Host $host;\n' \
                           '             proxy_set_header X-Real-IP $remote_addr;\n' \
                           '             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n' \
                           '             proxy_set_header X-Forwarded-Proto $scheme;\n' \
                           '             proxy_set_header X-Original-URL $request_uri;'

    def Update_content(self, contentk):
        if not isinstance(contentk, list): raise TypeError("contentk必须是一个列表")
        for content in contentk:
            if not isinstance(contentk, dict): raise TypeError("paths里的每一个参数必须是一个字典")
            self.content[content.get("key")] = content.get("data")

    def Set_listen(self, listen_port,server_name=None):
        self.listen_port = listen_port
        self.server_name=server_name
        if not self.server_name:self.server_name="localhost"
        # self.listen_port = int(listen_port)
        self.content["listen"] = self.listen_port

    def Set_Serverna(self, server_name):
        self.server_name = server_name
        self.content["server_name"] = self.server_name

    def Set_location(self, paths, tem_content=None):
        if not tem_content: tem_content = self.tem_content
        if not isinstance(paths, list): raise TypeError("paths必须是一个列表")
        for path in paths:
            if not isinstance(path, dict): raise TypeError("paths里的每一个参数必须是一个字典")
            self.location.append(
                f"\n         location {path.get('key')} {{\n"
                f"             proxy_pass {path.get('url')};\n"
                f"  {tem_content}\n         }}\n"
            )

    def Set_Crt(self,crt_path,key_path):
        self.ssl_content=f"         ssl_certificate   {crt_path};\n" \
                         f"         ssl_certificate_key  {key_path};\n" \
                         f"         ssl_session_cache  shared:SSL:1m;\n" \
                         f"         ssl_session_timeout  5m;\n" \
                         f"         ssl_ciphers HIGH:!aNULL:!MD5;\n" \
                         f"         ssl_prefer_server_ciphers  on;\n"
    def Set_Token(self,path,url,tem_content=None):
        if not tem_content: tem_content = self.tem_content
        self.token_url=url
        self.location.append(
            f"\n         location {url} {{\n"
            f"             internal;\n"
            f"             proxy_pass {path};\n"
            f"  {tem_content}\n         }}\n"
        )

    def Mapping(self, path, urls, tem_content=None,token=False):
        if not tem_content: tem_content = self.tem_content
        auth_content=""
        if token and self.token_url:
            auth_content=f"             auth_request {self.token_url};\n" \
                         f"             auth_request_set $auth_status $upstream_status;\n"
        if not isinstance(urls, list): raise TypeError("urls必须是一个列表")
        for url in urls:
            self.location.append(
                f"\n         location {url} {{\n"
                f"             {auth_content}"
                f"             proxy_pass {path};\n"
                f"  {tem_content}\n         }}\n"
            )

    def Add_Http_Redirect(self):
        """添加 HTTP 到 HTTPS 的重定向配置"""
        self.http_redirect = True

    def Save_Conf(self):
        # 确保使用正确的路径分隔符
        error_log = os.path.join(self.nginx_path, 'logs', 'error.log').replace('\\', '/')
        access_log = os.path.join(self.nginx_path, 'logs', 'access.log').replace('\\', '/')
        
        con_str = f"worker_processes {self.content.get('worker_processes')};\n" \
                  f"events {{\n" \
                  f"      worker_connections {self.content.get('worker_connections')};\n" \
                  f"}}\n"

        con_str += f"http {{\n" \
                   f"     include     {self.content.get('include')};\n" \
                   f"     default_type    {self.content.get('default_type')};\n" \
                   f"     sendfile    {self.content.get('sendfile')};\n" \
                   f"     keepalive_timeout    {self.content.get('keepalive_timeout')};\n" \
                   f"     error_log    {error_log};\n" \
                   f"     access_log   {access_log};\n"
        if self.listen_port and self.server_name:
            # HTTPS server
            if self.ssl_content:
                con_str += f"\n     server {{\n" \
                          f"         listen      {self.listen_port};\n" \
                          f"         server_name     {self.server_name};\n" \
                          f"{self.ssl_content}"
                
                if self.location != [] and len(self.location) >= 1:
                    for location in self.location:
                        con_str += location
                
                con_str += f"         error_page  {self.content.get('error_page')};\n"
                con_str += f"         location = {self.content.get('location')}\n     }}\n"

                # HTTP 重定向服务器
                if self.http_redirect:
                    con_str += f"\n     server {{\n" \
                              f"         listen      80;\n" \
                              f"         server_name     {self.server_name};\n" \
                              f"         return 301 https://$server_name$request_uri;\n" \
                              f"     }}\n"
            else:
                # 普通 HTTP server
                con_str += f"\n     server {{\n" \
                          f"         listen      {self.listen_port};\n" \
                          f"         server_name     {self.server_name};\n"
                
                if self.location != [] and len(self.location) >= 1:
                    for location in self.location:
                        con_str += location
                
                con_str += f"         error_page  {self.content.get('error_page')};\n"
                con_str += f"         location = {self.content.get('location')}\n     }}\n"

        con_str += "}\n"

        with open(self.config_path, 'w') as f:
            f.write(con_str)
        f.close()

    def Run_nginx(self):
        try:
            self.Save_Conf()
            nginx_exe = os.path.join(self.nginx_path, 'nginx.exe')
            print(f"正在启动 Nginx: {nginx_exe}")
            
            # 先尝试停止已运行的实例
            try:
                self.Stop_nginx()
                print("已停止旧的 Nginx 实例")
            except:
                pass
            
            # 检查可执行文件
            if not os.path.exists(nginx_exe):
                raise FileNotFoundError(f"找不到 Nginx 可执行文件: {nginx_exe}")
            
            # 检查配置文件
            if not os.path.exists(self.config_path):
                raise FileNotFoundError(f"找不到配置文件: {self.config_path}")
            
            # 确保日志目录存在
            log_dir = os.path.dirname(self.error_log)
            if not os.path.exists(log_dir):
                os.makedirs(log_dir)
            
            # 先测试配置文件
            test_process = subprocess.Popen(
                [nginx_exe, '-t', '-c', self.config_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                cwd=self.nginx_path
            )
            test_stdout, test_stderr = test_process.communicate()
            
            if test_process.returncode != 0:
                raise Exception(f"Nginx 配置测试失败: {test_stderr}")
            
            # 启动 Nginx（不等待返回）
            process = subprocess.Popen(
                [nginx_exe, '-c', self.config_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                cwd=self.nginx_path
            )
            
            # 等待一小段时间
            import time
            time.sleep(1)
            
            # 检查是否成功启动
            if process.poll() is not None:
                # 如果进程已经结束，说明启动失败
                _, stderr = process.communicate()
                raise Exception(f"Nginx 启动失败: {stderr}")
                
            print("Nginx 启动成功")
            
        except Exception as e:
            print(f"启动 Nginx 时出错: {str(e)}")
            raise

    def Run_nginx_only(self):
        process = subprocess.Popen([os.path.join(self.nginx_path, 'nginx.exe'), '-c', self.config_path],
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, cwd=self.nginx_path)
        # 读取输出和错误信息
        stdout, stderr = process.communicate()

    def Stop_nginx(self):
        """停止Nginx服务"""
        try:
            # 首先尝试正常停止
            os.system(f"{self.nginx_path}/nginx.exe -s stop")
            
            # 等待一小段时间
            import time
            time.sleep(1)
            
            # 确保所有nginx进程都已经停止
            for proc in psutil.process_iter(['pid', 'name']):
                if 'nginx' in proc.info['name'].lower():
                    try:
                        proc.kill()
                    except:
                        pass
                    
            # 清理pid文件
            pid_file = os.path.join(self.nginx_path, 'logs', 'nginx.pid')
            if os.path.exists(pid_file):
                os.remove(pid_file)
            
        except Exception as e:
            print(f"停止Nginx时出错: {str(e)}")

    def Reload_nginx(self):
        process = subprocess.Popen(
            [os.path.join(self.nginx_path, 'nginx.exe'), '-s', 'reload'],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            cwd=self.nginx_path  # 设置工作目录
        )
        stdout, stderr = process.communicate()




